package com.jztd.servone.invoice.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jztd.servone.emun.InvoicePartyCodeEnum;
import com.jztd.servone.emun.InvoiceStatusEnum;
import com.jztd.servone.invoice.dto.InvoiceRecordDTO;
import com.jztd.servone.invoice.dto.PTConfigDTO;
import com.jztd.servone.invoice.dto.ZPYConfigDTO;
import com.jztd.servone.invoice.param.ApplyInvoiceDetailsParam;
import com.jztd.servone.invoice.param.CancelInvoiceParam;
import com.jztd.servone.invoice.po.InvoicePartyConfig;
import com.jztd.servone.invoice.po.InvoiceRecord;
import com.jztd.servone.invoice.service.InvoicePartyConfigService;
import com.jztd.servone.invoice.service.InvoiceRecordService;
import com.jztd.servone.invoice.service.InvoiceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**臻票云开具发票*/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ZGZZInvoiceServiceImpl implements InvoiceService {
    private final InvoicePartyConfigService invoicePartyConfigService;
    private final InvoiceRecordService invoiceRecordService;

    @Override
    public InvoiceRecord applyInvoice(ApplyInvoiceDetailsParam param) {
        invoiceRecordService.checkInvoiceResults(param.getInvoiceCodes());
        InvoiceRecord invoiceRecord = Convert.convert(InvoiceRecord.class, param);
        InvoicePartyConfig invoicePartyConfig = invoicePartyConfigService.getInvoicePartyConfig(param.getClientCode());
        ZPYConfigDTO configDTO = Convert.convert(ZPYConfigDTO.class, JSONUtil.parseObj(invoicePartyConfig.getConfigData()));
        String invoiceReqSerialNo = this.generateFpqqlsh();
        JSONObject content = new JSONObject();
        content.set("sync", true);
        content.set("gfxxqr", 0); // 不强制校验购方信息
        content.set("fpqqlsh", invoiceReqSerialNo); // 生成流水号
        content.set("xfmc", configDTO.getXfmc());
        content.set("nsrsbh", configDTO.getNsrsbh());
        content.set("hsbz", "1"); // 含税
        content.set("fplxdm", "82"); // 数电普票
        content.set("tdyslxdm", "09"); // 特定类型代码：旅客运输
        content.set("yhlx", "0"); // 默认非自助开票
        content.set("gfmc", param.getBuyerName());
        content.set("gfsh", param.getBuyerTaxpayerNum());
        // ========== 构造商品明细 ==========
        // 金额（不含税金额）= 价税合计 ÷ (1 + 税率)
        BigDecimal je = param.getTotalAmount().divide(configDTO.getTaxRate().add(BigDecimal.ONE),2,RoundingMode.HALF_UP);
        JSONArray mxList = this.buildMxList(
                param.getProjectName(),       // 商品名称
                "3010101020101010000", // 商品编码
                param.getTotalAmount(),            // 金额
                configDTO.getTaxRate(),             // 税率
                param.getTotalAmount().subtract(je)        // 税额
        );
        content.set("mxList", mxList);
        //        // ========== 构造旅客运输服务(实名开票) ==========
        if(ObjectUtil.isNotEmpty(param.getTraveler())){
            JSONArray lkysfw = new JSONArray();
            JSONObject item = new JSONObject();
            item.set("cfd", param.getDeparturePlace());
            item.set("ddd", param.getDestinationPlace());
            item.set("cxr", param.getTraveler());
            item.set("cxrq", param.getTravelDate().replace("-",""));
            item.set("cxrzjhm", param.getTravelerIdNo());
            item.set("cxrzjlx", "1"); // 默认身份证
            item.set("jtgjlx", "4"); // 默认交通工具类型：4
            lkysfw.add(item);
            content.set("lkysfw", lkysfw);
        }
        // ========== 可选回调和邮箱信息 ==========
        JSONObject spExt = new JSONObject();
        spExt.set("hddz", "");
        spExt.set("yxdz", param.getTakerEmail());
        content.set("spExt", spExt);
        // ========== 构造完整请求体 ==========
        String serviceId = "FPKJ";
        JSONObject body = this.buildInvoiceBody(serviceId, configDTO.getDlzh(), configDTO.getNsrsbh(), content);
        // ========== 发起开票请求 ==========
        String response = this.sendInvoice(invoicePartyConfig, body);
        // ========== 打印结果 ==========
        System.out.println("开票接口响应结果：");
        System.out.println(response);
        JSONObject json = JSONUtil.parseObj(response);
        String status = json.getStr("code").equals("0")? InvoiceStatusEnum.SUCCESS.getCode()
                : json.getStr("code").equals("300077")
                ? InvoiceStatusEnum.IN_PROGRESS.getCode() : InvoiceStatusEnum.FAIL.getCode();
        invoiceRecord.setInvoiceStatus(status);
        invoiceRecord.setInvoiceReqSerialNo(invoiceReqSerialNo);
        invoiceRecord.setInvoiceAmount(param.getTotalAmount());
        invoiceRecord.setTaxRateValue(configDTO.getTaxRate());
        invoiceRecord.setTradeNo(param.getInvoiceCodes());
        invoiceRecord.setInvoiceTime(new Date());
        invoiceRecord.setGoodsName(param.getProjectName());
        return invoiceRecord;
    }

    @Override
    public Boolean cancelInvoice(CancelInvoiceParam param) {
        InvoicePartyConfig invoicePartyConfig = invoicePartyConfigService.getInvoicePartyConfig(param.getClientCode());
        ZPYConfigDTO configDTO = Convert.convert(ZPYConfigDTO.class, invoicePartyConfig);
        String serviceId = "HZXXBSC"; // 业务ID
//        String dlzh = "18108086641"; // 登录账号
//        String nsrsbh = "91510100MA6ADGBR27"; // 销方纳税人识别号

        JSONObject contentObj = new JSONObject();
        contentObj.set("chyydm", "01");  // 作废原因
        contentObj.set("hzcxje", "-9.71"); // 红字金额（不含税）
        contentObj.set("hzcxse", "-0.29"); // 红字税额
        contentObj.set("sqly", "0");        // 申请来源
        contentObj.set("xfsh", configDTO.getXfmc());    // 销售方税号
        contentObj.set("yfphm", param.getInvoiceRecordId()); // 原发票号码
        contentObj.set("yfplxdm", "82");   // 原蓝票发票类型代码
        contentObj.set("ykprq", "2025-08-05 17:20:21"); // 原开票日期

        JSONObject body = new JSONObject();
        body.set("serviceId", serviceId);
        body.set("dlzh", configDTO.getDlzh());
        body.set("nsrsbh", configDTO.getNsrsbh());
        body.set("content", contentObj);

        System.out.println("请求参数：" + body.toString());

        String response = this.sendInvoiceRed(invoicePartyConfig, body);

        System.out.println("臻票云响应：" + response);
        return true;
    }

    @Override
    public InvoiceRecordDTO getInvoiceDetails(String invoiceCode) {
        return null;
    }

    @Override
    public String getServiceType() {
        return InvoicePartyCodeEnum.ZPY_ZGZZ.name();
    }
    /**
     * 生成随机发票请求流水号（fpqqlsh）
     * 格式示例：isvkpXXXXXX
     * @return 随机流水号
     */
    private static final int RANDOM_STRING_LENGTH = 8;
    private static final Random RANDOM = new Random();
    private static final String ALLOWED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    public static String generateFpqqlsh() {
        StringBuilder sb = new StringBuilder("isvkp");
        for (int i = 0; i < RANDOM_STRING_LENGTH; i++) {
            int index = RANDOM.nextInt(ALLOWED_CHARS.length());
            sb.append(ALLOWED_CHARS.charAt(index));
        }
        return sb.toString();
    }
    /**
     * 示例：构建商品明细列表
     * @param spmc 商品名称
     * @param spbm 商品编码
     * @param je 金额
     * @param sl 税率（如 0.03）
     * @param se 税额
     * @return 明细列表
     */
    public JSONArray buildMxList(String spmc, String spbm, BigDecimal je, BigDecimal sl, BigDecimal se) {
        JSONArray array = new JSONArray();
        JSONObject item = new JSONObject();
        item.set("fphxz", "0"); // 正常行
        item.set("spmc", spmc);
        item.set("spbm", spbm);
        item.set("je", je);
        item.set("sl", sl);
        item.set("se", se);
        item.set("dj", ""); // 单价可留空
        item.set("spsl", ""); // 数量可留空
        array.add(item);
        return array;
    }
    /**
     * 构建发票请求参数体
     * @param serviceId 业务ID
     * @param dlzh 登录账号
     * @param nsrsbh 纳税人识别号
     * @param content 发票具体内容（调用方构建）
     * @return 请求 JSON 对象
     */
    private JSONObject buildInvoiceBody(String serviceId, String dlzh, String nsrsbh, JSONObject content) {
        JSONObject body = new JSONObject();
        body.set("serviceId", serviceId);
        body.set("dlzh", dlzh);
        body.set("nsrsbh", nsrsbh);
        body.set("content", content);
        return body;
    }
    /**
     * 发送发票开具请求
     * 自动添加鉴权 header、签名等内容
     * @param bodyJson 发票业务请求体（含 serviceId/dlzh/nsrsbh/content）
     * @return 接口响应字符串
     */
    private String sendInvoice(InvoicePartyConfig invoicePartyConfig,JSONObject bodyJson) {
        long timestamp = System.currentTimeMillis();
        String date = String.valueOf(timestamp);
        String accessToken = getToken(invoicePartyConfig);

        String sign = Base64.encodeBase64String(
                DigestUtils.md5Hex(bodyJson.toString() + date + accessToken).getBytes()
        );

        String result =HttpRequest.post(invoicePartyConfig.getUrl() + "/invoiceEx/zpy/electric/invoice/issued")
                .header("AppId", invoicePartyConfig.getAppId())
                .header("Date", date)
                .header("Content-MD5", sign)
                .header("Content-Type", "application/json")
                .body(bodyJson.toString())
                .execute()
                .body();
        return result;
    }
    /**
     * 发送发票冲红请求
     * @param bodyJson 请求体JSON对象
     * @return 响应字符串
     */
    private String sendInvoiceRed(InvoicePartyConfig invoicePartyConfig, JSONObject bodyJson) {
        long timestamp = System.currentTimeMillis();
        String date = String.valueOf(timestamp);

        String accessToken = getToken(invoicePartyConfig);

        String sign = Base64.encodeBase64String(
                DigestUtils.md5Hex(bodyJson.toString() + date + accessToken).getBytes()
        );

        Map<String, String> headers = new HashMap<>();
        headers.put("AppId", invoicePartyConfig.getAppId());
        headers.put("Date", date);
        headers.put("Content-MD5", sign);
        headers.put("Content-Type", "application/json");

        String result =HttpRequest.post(invoicePartyConfig.getUrl() + "/invoiceEx/zpy/electric/invoice/applyRedInformation")
                .header("AppId", invoicePartyConfig.getAppId())
                .header("Date", date)
                .header("Content-MD5", sign)
                .header("Content-Type", "application/json")
                .body(bodyJson.toString())
                .execute()
                .body();
        return "";
    }
    /**
     * 获取 accessToken，用于接口调用鉴权
     * @return accessToken 字符串
     */
    public static String getToken(InvoicePartyConfig invoicePartyConfig) {
        ZPYConfigDTO configDTO = Convert.convert(ZPYConfigDTO.class, JSONUtil.parseObj(invoicePartyConfig.getConfigData()));
        String url = configDTO.getAccessTokenUrl()+"?appId=" + invoicePartyConfig.getAppId() + "&appScrt=" + invoicePartyConfig.getPassword();
        String response = HttpUtil.get(url);
        JSONObject json = new JSONObject(response);
        return json.getJSONObject("result").getStr("accessToken");
    }
}
